package at.ac.tuwien.swa.sparrow.server.controller

import ac.at.tuwien.infosys.swa.audio.Fingerprint
import at.ac.tuwien.swa.sparrow._
import at.ac.tuwien.swa.sparrow.common._
import at.ac.tuwien.swa.sparrow.server.ClientService
import at.ac.tuwien.swa.sparrow.server.component._
import at.ac.tuwien.swa.sparrow.server.persistence.AccountRepository
import at.ac.tuwien.swa.sparrow.server.security.{SecurityContext, Secured}
import com.typesafe.scalalogging.log4j.Logging
import java.util.UUID
import java.util.concurrent.atomic.AtomicLong
import javax.inject.Inject
import org.apache.commons.lang3.time.DateFormatUtils.{ISO_DATETIME_FORMAT => DF}
import org.springframework.stereotype.Controller

@Controller
class ClientController @Inject()(peerManager: PeerManager, userManager: UserManager, billingEngine: BillingEngine,
																 accountRepository: AccountRepository)
	extends ClientService with Logging {

	def account = SecurityContext.getRole.asInstanceOf[Account]

	@Secured() override def login(username: String, password: String) = {
		accountRepository findOne(username, password) match {
			case None => throw AccessDeniedException()
			case Some(a: Account) => val token = AuthToken(a.id, UUID.randomUUID.toString)
				userManager.login(username, token)
				token
		}
	}

	@Secured(classOf[Account]) override def logout() {
		userManager.logout(account.username)
	}

	@Secured() override def register(username: String, password: String) = {
		accountRepository findOne(username, null) match {
			case Some(a) => throw InvalidCredentialsException()
			case None => val a = Account(username, password, new AtomicLong(WELCOME_CREDITS))
				(accountRepository save a) == a
		}
	}

	@Secured(classOf[Account]) override def token(oldToken: AuthToken) = {
		val token = oldToken.copy(timestamp = System.currentTimeMillis)
		logger.debug(s"Updating lease of $oldToken from ${DF format oldToken.timestamp} to ${DF format token.timestamp}")
		userManager.tokens.removeBinding(account.username, oldToken)
		userManager.tokens.addBinding(account.username, token)
		token
	}

	@Secured(classOf[Account]) override def buy(amount: Int) = billingEngine.buy(account.id, amount)

	@Secured(classOf[Account]) override def request(authToken: AuthToken, fp: Fingerprint): Ticket = {
		userManager.tokens find (_._2.contains(authToken)) match {
			case None => throw AccessDeniedException()
			case Some((username, tokens)) => {
				accountRepository findOne username match {
					case None => throw new IllegalStateException()
					case Some(a: Account) if a.credits.get() < SEARCH_COSTS => throw NotEnoughCreditsException(SEARCH_COSTS - a.credits.get())
					case Some(a: Account) => peerManager.request(authToken, fp)
				}
			}
		}
	}

	@Secured(classOf[Account]) def result(ticketId: UUID) = {
		val found = peerManager.tickets find (t => t._1.id == ticketId)
		found match {
			case None => throw new IllegalArgumentException()
			case Some((ticket, info)) if info.isDefined => peerManager.tickets -= ticket; TrackResult(info.get, Found)
			case Some((ticket, title)) if !ticket.isValid => peerManager.tickets -= ticket; TrackResult(title getOrElse null, Invalid)
			case Some((ticket, title)) if ticket.isExpired => peerManager.tickets -= ticket; TrackResult(title getOrElse null, NotFound)
			case Some((_, title)) => TrackResult(title getOrElse null, InProgress)
		}
	}
}
